package com.phiture.erp.finance.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.phiture.erp.basic.api.pojo.resp.ErpCustomerResp;
import com.phiture.erp.basic.api.pojo.resp.ErpSupplierResp;
import com.phiture.erp.basic.api.service.ErpCustomerApi;
import com.phiture.erp.basic.api.service.ErpSupplierApi;
import com.phiture.erp.common.constants.ErpNoPrefixConstants;
import com.phiture.erp.common.enums.ErpAuditStatus;
import com.phiture.erp.common.utils.ErpNoGenerator;
import com.phiture.erp.finance.common.enums.ErpFinanceStatementSelectedEnum;
import com.phiture.erp.finance.common.enums.ErpFinanceStatementStatusEnum;
import com.phiture.erp.finance.common.enums.ErpFinanceStatementTypeEnum;
import com.phiture.erp.finance.core.pojo.dto.PageErpFinanceStatementDTO;
import com.phiture.erp.finance.core.pojo.vo.ErpFinanceStatementPageReqVO;
import com.phiture.erp.finance.core.pojo.vo.ErpFinanceStatementSaveReqVO;
import com.phiture.erp.finance.core.pojo.vo.ErpFinanceStatementVO;
import com.phiture.erp.finance.core.service.ErpFinanceStatementService;
import com.phiture.erp.finance.dal.entity.ErpFinancePaymentItemDO;
import com.phiture.erp.finance.dal.entity.ErpFinanceReceiptItemDO;
import com.phiture.erp.finance.dal.entity.ErpFinanceStatementDO;
import com.phiture.erp.finance.dal.entity.ErpFinanceStatementItemsDO;
import com.phiture.erp.finance.dal.mapper.ErpFinancePaymentItemMapper;
import com.phiture.erp.finance.dal.mapper.ErpFinanceReceiptItemMapper;
import com.phiture.erp.finance.dal.mapper.ErpFinanceStatementItemsMapper;
import com.phiture.erp.finance.dal.mapper.ErpFinanceStatementMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.phiture.erp.common.constants.ErrorCodeConstants.*;
import static com.phiture.erp.finance.common.enums.ErpFinanceStatementStatusEnum.TO_BE_CONFIRMED;

/**
 * 对账单 Service 实现类
 *
 * @author 芋道源码
 */

@Slf4j
@Service
public class ErpFinanceStatementServiceImpl implements ErpFinanceStatementService {

    @Resource
    private ErpFinanceStatementMapper financeStatementMapper;
    @Resource
    private ErpFinanceStatementItemsMapper financeStatementItemsMapper;
    @Resource
    private ErpNoGenerator erpNoGenerator;
    @Resource
    private ErpSupplierApi supplierApi;
    @Resource
    private ErpCustomerApi customerApi;
    @Resource
    private ErpFinancePaymentItemMapper paymentItemMapper;
    @Resource
    private ErpFinanceReceiptItemMapper receiptItemMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createFinanceStatement(ErpFinanceStatementSaveReqVO createReqVO) {
        // 插入
        ErpFinanceStatementDO financeStatement = BeanUtils.toBean(createReqVO, ErpFinanceStatementDO.class);
        financeStatementMapper.insert(financeStatement);

        // 插入子表
        createFinanceStatementItemsList(financeStatement.getId(), createReqVO.getFinanceStatementItemss());
        // 返回
        return financeStatement.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateFinanceStatement(ErpFinanceStatementSaveReqVO updateReqVO) {
        // 校验存在
        validateFinanceStatementExists(updateReqVO.getId());
        // 更新
        ErpFinanceStatementDO updateObj = BeanUtils.toBean(updateReqVO, ErpFinanceStatementDO.class);
        financeStatementMapper.updateById(updateObj);

        // 更新子表
        updateFinanceStatementItemsList(updateReqVO.getId(), updateReqVO.getFinanceStatementItemss());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateFinanceStatementStatus(Long id, Integer status) {
        boolean approve = ErpFinanceStatementStatusEnum.CONFIRMED.getStatus().equals(status);
        // 1.1 校验存在
        ErpFinanceStatementDO statementDO = validateFinanceStatementExists(id);
        // 1.2 校验状态  
        if (statementDO.getStatus().equals(status)) {
            throw exception(approve ? FINANCE_STATEMENT_APPROVE_FAIL : FINANCE_STATEMENT_PROCESS_FAIL);
        }

        if(TO_BE_CONFIRMED.getStatus().equals(status)){
            if(ErpFinanceStatementTypeEnum.isSupplier(statementDO.getType())){
                boolean exists = paymentItemMapper.exists(Wrappers.lambdaQuery(ErpFinancePaymentItemDO.class).eq(ErpFinancePaymentItemDO::getBizNo, statementDO.getNo()));
                if(exists){
                    throw exception(FINANCE_STATEMENT_PROCESS_FAIL_EXISTS_PAYMENT);
                }
            } else if(ErpFinanceStatementTypeEnum.isCustomer(statementDO.getType())){
                boolean exists = receiptItemMapper.exists(Wrappers.lambdaQuery(ErpFinanceReceiptItemDO.class).eq(ErpFinanceReceiptItemDO::getBizNo, statementDO.getNo()));
                if(exists){
                    throw exception(FINANCE_STATEMENT_PROCESS_FAIL_EXISTS_RECEIPT);
                }
            }
        }

        // 2. 更新状态
        statementDO.setStatus(status);
        int updateCount = financeStatementMapper.updateById(statementDO);
        if (updateCount == 0) {
            throw exception(approve ? FINANCE_STATEMENT_APPROVE_FAIL : FINANCE_STATEMENT_PROCESS_FAIL);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteFinanceStatement(Long id) {
        // 校验存在
        ErpFinanceStatementDO statementDO = validateFinanceStatementExists(id);

        // 供应商对账单删除后，采购入库单和采购退货单的对账状态更新为未对账
        if (statementDO.getType().equals(ErpFinanceStatementTypeEnum.SUPPLIER_PAYMENT.getType())) {
            // 1. 处理采购入库单
            // 根据对账单ID获取对账单明细
            List<ErpFinanceStatementItemsDO> itemsList = financeStatementItemsMapper.selectListByStatementId(id);
            // 获取所有采购入库单ID, 同时billingQuantity大于0的
            List<Long> purchaseInIds = itemsList.stream()
                    .filter(item -> item.getSourceId() != null && item.getBillingQuantity().compareTo(BigDecimal.ZERO) > 0)
                    .map(ErpFinanceStatementItemsDO::getSourceId)
                    .distinct()
                    .collect(Collectors.toList());
            // 更新采购入库单的对账状态
            if (!purchaseInIds.isEmpty()) {
                // 更新采购入库单的对账状态为未对账
                financeStatementMapper.updatePurchaseInStatusByIds(purchaseInIds, ErpFinanceStatementSelectedEnum.NO.getStatus(), TenantContextHolder.getTenantId());
            }

            // 2. 处理采购退货单
            // 获取所有采购退货单ID, 同时billingQuantity小于0的
            List<Long> purchaseReturnIds = itemsList.stream()
                    .filter(item -> item.getSourceId() != null && item.getBillingQuantity().compareTo(BigDecimal.ZERO) < 0)
                    .map(ErpFinanceStatementItemsDO::getSourceId)
                    .distinct().collect(Collectors.toList());
            // 更新采购退货单的对账状态
            if (!purchaseReturnIds.isEmpty()) {
                financeStatementMapper.updatePurchaseReturnStatusByIds(purchaseReturnIds, ErpFinanceStatementSelectedEnum.NO.getStatus(), TenantContextHolder.getTenantId());
            }

        }

        // 删除
        financeStatementMapper.deleteById(id);
        // 删除子表
        deleteFinanceStatementItemsByStatementId(id);
    }

    private ErpFinanceStatementDO validateFinanceStatementExists(Long id) {
        ErpFinanceStatementDO financeStatement = financeStatementMapper.selectById(id);
        if (financeStatement == null) {
            throw exception(FINANCE_STATEMENT_NOT_EXISTS);
        }
        return financeStatement;
    }

    @Override
    public ErpFinanceStatementDO getFinanceStatement(Long id) {
        return financeStatementMapper.selectById(id);
    }

    @Override
    public PageResult<ErpFinanceStatementDO> getFinanceStatementPage(ErpFinanceStatementPageReqVO pageReqVO) {
        PageResult<ErpFinanceStatementDO> result = financeStatementMapper.selectPage(pageReqVO, new LambdaQueryWrapperX<ErpFinanceStatementDO>()
                .eqIfPresent(ErpFinanceStatementDO::getNo, pageReqVO.getNo())
                .eqIfPresent(ErpFinanceStatementDO::getType, pageReqVO.getType())
                .eqIfPresent(ErpFinanceStatementDO::getBillingPeriod, pageReqVO.getBillingPeriod())
                .eqIfPresent(ErpFinanceStatementDO::getTransactionUserId, pageReqVO.getSupplierId())
                .eqIfPresent(ErpFinanceStatementDO::getTransactionUserId, pageReqVO.getCustomerId())
                .betweenIfPresent(ErpFinanceStatementDO::getStatementDate, pageReqVO.getStatementDate())
                .eqIfPresent(ErpFinanceStatementDO::getStatus, pageReqVO.getStatus())
                .orderByDesc(ErpFinanceStatementDO::getId));

        return result;
    }

    // ==================== 子表（对账单明细） ====================

    @Override
    public List<ErpFinanceStatementItemsDO> getFinanceStatementItemsListByStatementId(Long statementId) {
        return financeStatementItemsMapper.selectListByStatementId(statementId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean generateFinanceStatement(ErpFinanceStatementPageReqVO reqVO) {
        // 获取对账周期
        String billingPeriod = reqVO.getBillingPeriod();
        // 获取供应商
        Long supplierId = reqVO.getSupplierId();
        // 获取类型
        Integer type = reqVO.getType();

        Boolean result = Boolean.FALSE;

        // 根据类型处理不同对账单逻辑
        if (type == ErpFinanceStatementTypeEnum.SUPPLIER_PAYMENT.getType()) {
            // 处理供应商对账单逻辑
            result = generateFinanceStatementBySupplier(supplierId, billingPeriod);
        } else if (type == ErpFinanceStatementTypeEnum.CUSTOMER_RECEIPT.getType()) {
            // 处理客户对账单逻辑
            result = generateFinanceStatementByCustomer(supplierId, billingPeriod);
        }

        return result;
    }

    @Override
    public List<ErpFinanceStatementDO> batchSaveFinanceStatement(List<ErpFinanceStatementDO> list) {
        financeStatementMapper.insertBatch(list);
//        if(!batch){
//            log.error("[ErpFinanceStatementService.batchSave] 批量保存对账单明细异常，list:{}", JSONUtil.toJsonStr(list));
//            throw exception(FINANCE_STATEMENT_BATCH_SAVE_ERROR);
//        }
        return list;
    }

    @Override
    public void batchSaveFinanceStatementItem(List<ErpFinanceStatementItemsDO> list) {
        financeStatementItemsMapper.insertBatch(list);
    }

    @Override
    public PageResult<ErpFinanceStatementVO> pageFinanceStatement(PageErpFinanceStatementDTO dto) {
        List<Integer> statusList = dto.getStatus() == null
                ? Arrays.asList(ErpFinanceStatementStatusEnum.CONFIRMED.getStatus(), ErpFinanceStatementStatusEnum.PARTIAL_SETTLEMENT.getStatus())
                : Collections.singletonList(dto.getStatus());
        PageResult<ErpFinanceStatementDO> result = financeStatementMapper.selectPage(dto, new LambdaQueryWrapperX<ErpFinanceStatementDO>()
                .eq(ErpFinanceStatementDO::getType, dto.getFinanceStatementType())
                .eqIfPresent(ErpFinanceStatementDO::getNo, dto.getFinanceStatementNo())
                .eqIfPresent(ErpFinanceStatementDO::getTransactionUserId, dto.getTransactionUserId())
                .eqIfPresent(ErpFinanceStatementDO::getBillingPeriod, dto.getBillingPeriod())
                .in(ErpFinanceStatementDO::getStatus, statusList)
                .orderByDesc(ErpFinanceStatementDO::getId));

        List<ErpFinanceStatementDO> list = result.getList();
        if (CollUtil.isEmpty(list)) {
            return new PageResult<>(Collections.emptyList(), result.getTotal());
        }

        List<ErpFinanceStatementVO> voList = list.stream().map(item -> {
            ErpFinanceStatementVO vo = new ErpFinanceStatementVO();
            vo.setFinanceStatementId(item.getId());
            vo.setFinanceStatementNo(item.getNo());
            vo.setType(item.getType());
            vo.setBillingPeriod(item.getBillingPeriod());
            vo.setStatementDate(item.getStatementDate());
            vo.setTotalAmount(item.getTotalAmount());
            vo.setTransactionAmount(item.getTransactionAmount());
            vo.setStatus(item.getStatus());
            vo.setTransactionUserId(item.getTransactionUserId());
            return vo;
        }).collect(Collectors.toList());

        Set<Long> transactionUserIds = voList.stream().map(ErpFinanceStatementVO::getTransactionUserId).collect(Collectors.toSet());
        if (ErpFinanceStatementTypeEnum.isSupplier(dto.getFinanceStatementType())) {
            Map<Long, ErpSupplierResp> supplierMap = supplierApi.getSupplierMap(transactionUserIds);
            voList.forEach(vo -> {
                ErpSupplierResp supplier = supplierMap.get(vo.getTransactionUserId());
                if (supplier != null) {
                    vo.setTransactionUserName(supplier.getName());
                }
            });
        } else if (ErpFinanceStatementTypeEnum.isCustomer(dto.getFinanceStatementType())) {
            Map<Long, ErpCustomerResp> customerMap = customerApi.getCustomerMap(transactionUserIds);
            voList.forEach(vo -> {
                ErpCustomerResp customer = customerMap.get(vo.getTransactionUserId());
                if (customer != null) {
                    vo.setTransactionUserName(customer.getName());
                }
            });
        }
        return new PageResult<>(voList, result.getTotal());
    }

    @Override
    public List<ErpFinanceStatementDO> getListByFinanceStatementNos(Collection<String> financeStatementNos) {
        return financeStatementMapper.selectList(ErpFinanceStatementDO::getNo, financeStatementNos);
    }

    @Override
    public void removeFinanceStatementById(Long id) {
        financeStatementMapper.deleteById(id);
    }

    @Override
    public void removeFinanceStatementItemByItemIds(Collection<Long> itemIds) {
        financeStatementItemsMapper.deleteByIds(itemIds);
    }

    @Override
    public boolean existById(Long id) {
        return financeStatementMapper.exists(Wrappers.lambdaQuery(ErpFinanceStatementDO.class).eq(ErpFinanceStatementDO::getId, id));
    }

    @Override
    public void backfillTransactionAmount(List<ErpFinanceStatementDO> list) {
        Set<Long> ids = list.stream().map(ErpFinanceStatementDO::getId).collect(Collectors.toSet());
        List<ErpFinanceStatementDO> statementDOS = financeStatementMapper.selectList(ErpFinanceStatementDO::getId, ids);
        Map<Long, BigDecimal> transactionAmountMap = statementDOS.stream().collect(Collectors.toMap(ErpFinanceStatementDO::getId, ErpFinanceStatementDO::getTransactionAmount));
        list.forEach(item ->
            item.setTransactionAmount(
                    item.getTransactionAmount().add(transactionAmountMap.get(item.getId()))
            )
        );
        financeStatementMapper.updateBatch(list);
    }

    @Override
    public List<ErpFinanceStatementDO> getFinanceStatementList(Collection<Long> statementIds) {
        return financeStatementMapper.selectList(ErpFinanceStatementDO::getId, statementIds);
    }

    @Override
    public void batchChangeTransactionAmount(List<ErpFinanceStatementDO> list) {
        financeStatementMapper.updateBatch(list);
    }

    private Boolean generateFinanceStatementBySupplier(Long supplierId, String billingPeriod) {
        // 根据对账周期转换成开始时间和结束时间 如2025-06转成2025-06-01和2025-07-01
        String[] billingPeriodArray = billingPeriod.split("-");
        String startDate = billingPeriodArray[0] + "-" + billingPeriodArray[1] + "-01";
        String endDate = billingPeriodArray[0] + "-" + (Integer.parseInt(billingPeriodArray[1]) + 1) + "-01";
        // 根据供应商和对账周期查询采购入库单
        List<ErpFinanceStatementDO> statementDOList = financeStatementMapper.getPurchaseInByPeriod(
                billingPeriod,
                ErpAuditStatus.APPROVE.getStatus(),
                ErpFinanceStatementTypeEnum.SUPPLIER_PAYMENT.getType(),
                supplierId,
                TenantContextHolder.getTenantId(),
                startDate,
                endDate
        );

        // 根据供应商和对账周期查询采购退货单
        List<ErpFinanceStatementDO> returnStatementDOList = financeStatementMapper.getPurchaseReturnByPeriod(
                billingPeriod,
                ErpAuditStatus.APPROVE.getStatus(),
                ErpFinanceStatementTypeEnum.SUPPLIER_PAYMENT.getType(),
                supplierId,
                TenantContextHolder.getTenantId(),
                startDate,
                endDate
        );

        // 如果没有采购入库单，直接返回
        if (statementDOList.isEmpty() && returnStatementDOList.isEmpty()) {
            return Boolean.FALSE;
        } else {

            statementDOList.forEach(
                    statementDO -> {
                        // 自动生成 对账单 编号
                        String statementNo = erpNoGenerator.generate(ErpNoPrefixConstants.FINANCE_STATEMENT_SUPPLIER_NO_PREFIX);
                        validateMpNo(statementNo);
                        statementDO.setNo(statementNo);
                        statementDO.setStatementDate(LocalDateTime.now());
                        statementDO.setStatus(TO_BE_CONFIRMED.getStatus());

                        // 根据statementDO.供应商ID,查出退货单中是否有该供应商数据
                        if (!returnStatementDOList.isEmpty()) {
                            // 如果有退货单数据，则将退货单数据合并到对账单中
                            returnStatementDOList.forEach(returnStatementDO -> {
                                if (returnStatementDO.getTransactionUserId().equals(statementDO.getTransactionUserId())) {
                                    statementDO.setTotalAmount(statementDO.getTotalAmount().subtract(returnStatementDO.getTotalAmount()));
                                }
                            });
                        }

                        // 插入对账单
                        financeStatementMapper.insert(statementDO);

                        // 查询采购入库单明细
                        List<ErpFinanceStatementItemsDO> itemsList = financeStatementMapper.getPurchaseInItemsByPeriod(
                                statementDO.getTransactionUserId(),
                                ErpAuditStatus.APPROVE.getStatus(),
                                TenantContextHolder.getTenantId(),
                                startDate,
                                endDate
                        );

                        // 插入对账单明细
                        createFinanceStatementItemsList(statementDO.getId(), itemsList);

                        // 查询采购退货单明细
                        List<ErpFinanceStatementItemsDO> returnItemsList = financeStatementMapper.getPurchaseReturnItemsByPeriod(
                                statementDO.getTransactionUserId(),
                                ErpAuditStatus.APPROVE.getStatus(),
                                TenantContextHolder.getTenantId(),
                                startDate,
                                endDate
                        );
                        // 插入对账单明细
                        createFinanceStatementItemsList(statementDO.getId(), returnItemsList);

                        // 更新采购入库单和采购退货单的对账状态为已对账
                        // 过滤出所有的采购入库单和采购退货单的ID
                        List<Long> purchaseInIds = itemsList.stream().map(ErpFinanceStatementItemsDO::getSourceId).distinct().collect(Collectors.toList());
                        List<Long> purchaseReturnIds = returnItemsList.stream().map(ErpFinanceStatementItemsDO::getSourceId).distinct().collect(Collectors.toList());
                        // 更新采购入库单和采购退货单的对账状态为已对账
                        if (!purchaseInIds.isEmpty()) {
                            financeStatementMapper.updatePurchaseInStatusByIds(purchaseInIds, ErpFinanceStatementSelectedEnum.YES.getStatus(), TenantContextHolder.getTenantId());
                        }
                        if (!purchaseReturnIds.isEmpty()) {
                            financeStatementMapper.updatePurchaseReturnStatusByIds(purchaseReturnIds, ErpFinanceStatementSelectedEnum.YES.getStatus(), TenantContextHolder.getTenantId());
                        }
                    }
            );
        }
        return Boolean.TRUE;
    }

    private Boolean generateFinanceStatementByCustomer(Long customerId, String billingPeriod) {
        // 处理客户对账单逻辑
        return Boolean.TRUE;
    }


    private void validateMpNo(String statementNo) {
        boolean exists = financeStatementMapper.exists(
                Wrappers.lambdaQuery(ErpFinanceStatementDO.class)
                        .eq(ErpFinanceStatementDO::getNo, statementNo)
                        .eq(ErpFinanceStatementDO::getDeleted, Boolean.FALSE)
        );
        if (exists) {
            throw exception(FINANCE_STATEMENT_NO_EXISTS);
        }
    }

    private void createFinanceStatementItemsList(Long statementId, List<ErpFinanceStatementItemsDO> list) {
        list.forEach(o -> o.setStatementId(statementId));
        financeStatementItemsMapper.insertBatch(list);
    }

    private void updateFinanceStatementItemsList(Long statementId, List<ErpFinanceStatementItemsDO> list) {
        deleteFinanceStatementItemsByStatementId(statementId);
        list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下：1）id 冲突；2）updateTime 不更新
        createFinanceStatementItemsList(statementId, list);
    }

    private void deleteFinanceStatementItemsByStatementId(Long statementId) {
        financeStatementItemsMapper.deleteByStatementId(statementId);
    }

}